// Pavel Zolnikov[http://www.codeproject.com/script/profile/whos_who.asp?id=35980], 2002

using System;
using System.Runtime.InteropServices;

namespace VirusCleaner.Center
{
	/// <summary>
	/// Implements base functionality required for setting Windows Hooks (SetWindowsHookEx API)
	/// </summary>
	/// <example>
	/// 
	/// //Sample hook that raises Idle event when thread is about to go idle.
	/// //Can be usefull for procesing background lo priority tasks
	/// 	public class IdleHook : WindowsHook
	/// 	{
	/// 		public void Start()
	/// 		{
	/// 			UnHookMe();
	/// 			HookMe(11);//WH_FOREGROUNDIDLE
	/// 		}
	///   
	/// 		public void Stop()
	/// 		{
	/// 			UnHookMe();
	/// 		}
	/// 
	/// 		public event EventHandler Idle;
	/// 
	/// 		protected override Int32 HookCallBack(Int32 ncode, Int32 wParam, Int32 lParam)
	/// 		{
	/// 			if( Idle != null )
	/// 				Idle(this, EventArgs.Empty);
	/// 
	/// 			return base.HookCallBack(ncode, wParam, lParam);
	/// 		}
	/// 	}
	/// </example>
	abstract public class WindowsHook : MarshalByRefObject, IDisposable 
	{
		WindowsHook.WindowsHookCallBack hookDelegate;
		IntPtr hhook;

		/// <summary>
		///	Override this function to process hooks
		/// </summary>
		/// <remarks>
		/// Call <code>return base.HookCallBack(ncode, wParam, lParam)</code> at the end.
		/// </remarks>
		protected virtual Int32 HookCallBack(Int32 ncode, Int32 wParam, Int32 lParam)
		{
			GC.KeepAlive(this);//FxCop
			/// making sure that other hooks get chance to process event as well.
			return CallNextHookEx(hhook , ncode, wParam, lParam);
		}

		/// <summary>
		/// Hooks <code>HookCallBack</code> method to process event of type specified by <code>idHook</code>;
		/// </summary>
		/// <param name="idHook">See winuser.h for possible values(WH_something)</param>
		protected void HookMe(Int32 idHook)
		{
			GC.KeepAlive(this);
			hookDelegate = new WindowsHookCallBack(hookCallBack);

			hhook = SetWindowsHookEx( 
				idHook,
				hookDelegate,
				IntPtr.Zero,
				Win32.GetCurrentThreadId() );	
		}

		/// <summary>
		/// Stops processing events.
		/// </summary>
		protected void UnHookMe()
		{
			GC.KeepAlive(this);
			if( hhook != IntPtr.Zero )
			{
				UnhookWindowsHookEx( hhook );
				hhook = IntPtr.Zero;
			}

			if( hookDelegate != null )
				hookDelegate = null;				
		}

		/// <summary>
		/// Stops processing events, disposes of resources.
		/// </summary>
		public virtual void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			UnHookMe();
		}

		~WindowsHook()
		{
			Dispose(false);
		}

		/// <summary>
		/// WindowsHook callback prototype.
		/// </summary>
		delegate Int32 WindowsHookCallBack(Int32 ncode, Int32 wParam, Int32 lParam);

		/// <summary>
		/// Generic implementation of WindowsHook.
		/// </summary>
		/// <remarks>
		/// Checks ncode to be greater or equal to HC_ACTION before calling virtual HookCallBack.
		/// </remarks>
		Int32 hookCallBack(Int32 ncode, Int32 wParam, Int32 lParam)
		{
			GC.KeepAlive(this);
			return ncode >= 0 ?	//HC_ACTION, etc
				HookCallBack( ncode, wParam, lParam ) :
				CallNextHookEx(hhook , ncode, wParam, lParam);
		}

		[DllImport("user32.dll")]
		static extern IntPtr SetWindowsHookEx( Int32 idHook, WindowsHookCallBack lpfn, IntPtr hmodule, Int32 threadID);

		[DllImport("user32.dll")]
		static extern Int32 CallNextHookEx(IntPtr hhok, Int32 ncode, Int32 wParam, Int32 lParam);

		[DllImport("user32.dll")]
		static extern bool UnhookWindowsHookEx( IntPtr hhook );
	}
}